classdef BitSynchronization < Signal
	
properties (SetAccess = private)
	SVID;
	SecondaryPRNCodeObjs;
	CorrelatorOutBuffer;
	FrequencyShiftBuffer;
	CarrierPhaseShiftBuffer;
	TimeShiftBuffer;
	CorrelatorOutBufferSize;
	NumberOfCorrelatorOutInBuffer;
	BitSoftInformationFileName;
	BitSoftInformationFile;
	Replica;
	ProbabilityOfFalseAlarm;
	SufficientStatistics;
	Threshold;
end % end of properties

properties (SetAccess = private, GetAccess = private)
	checkBitSynchronizationHandle;
end % end of properties

methods 
	
	function obj = BitSynchronization(Name, SVID, BitSoftInformationFileName)
		obj = obj@Signal(Name);
		obj.SVID = SVID;
		obj.BitSoftInformationFileName = BitSoftInformationFileName;
		obj.BitSoftInformationFile = fopen(obj.BitSoftInformationFileName, 'w');
		obj.NumberOfCorrelatorOutInBuffer = 0;
		obj.ProbabilityOfFalseAlarm = 1e-9;
		SVID = obj.SVID;
		switch (obj.Name)
			case 'E5'
				obj.CorrelatorOutBufferSize = 100;
				N = obj.CorrelatorOutBufferSize;
				obj.SecondaryPRNCodeObjs{1} = SecondaryPRNCode('E5aI');
				obj.SecondaryPRNCodeObjs{2} = SecondaryPRNCode('E5aQ');
				obj.SecondaryPRNCodeObjs{3} = SecondaryPRNCode('E5bI');
				obj.SecondaryPRNCodeObjs{4} = SecondaryPRNCode('E5bQ');
				obj.Replica(1,:) = BitSynchronization.extendCode(...
													 obj.SecondaryPRNCodeObjs{1}.getCode(1, 0), N);
				obj.Replica(2,:) = BitSynchronization.extendCode(...
													 obj.SecondaryPRNCodeObjs{2}.getCode(SVID, 0), N);
				obj.Replica(3,:) = BitSynchronization.extendCode(...
													 obj.SecondaryPRNCodeObjs{3}.getCode(1, 0), N);
				obj.Replica(4,:) = BitSynchronization.extendCode(...
													 obj.SecondaryPRNCodeObjs{4}.getCode(SVID, 0), N);
				obj.Threshold = zeros(1, 4);
				obj.SufficientStatistics = zeros(1, 4);
				obj.checkBitSynchronizationHandle = @obj.checkBitSynchronizationE5;
			case 'E5a'
				obj.CorrelatorOutBufferSize = 100;
				N = obj.CorrelatorOutBufferSize;
				obj.SecondaryPRNCodeObjs{1} = SecondaryPRNCode('E5aI');
				obj.SecondaryPRNCodeObjs{2} = SecondaryPRNCode('E5aQ');
				obj.Replica(1,:) = BitSynchronization.extendCode(...
													 obj.SecondaryPRNCodeObjs{1}.getCode(1, 0), N);
				obj.Replica(2,:) = BitSynchronization.extendCode(...
													 obj.SecondaryPRNCodeObjs{2}.getCode(SVID, 0), N);
				obj.Threshold = zeros(1, 2);
				obj.SufficientStatistics = zeros(1, 2);
				obj.checkBitSynchronizationHandle = @obj.checkBitSynchronizationE5a;
			case 'E5aI'
				obj.CorrelatorOutBufferSize = 100;
				N = obj.CorrelatorOutBufferSize;
				obj.SecondaryPRNCodeObjs{1} = SecondaryPRNCode('E5aI');
				obj.Replica(1,:) = BitSynchronization.extendCode(...
													 obj.SecondaryPRNCodeObjs{1}.getCode(1, 0), N);
				obj.Threshold = zeros(1, 1);
				obj.SufficientStatistics = zeros(1, 1);
				obj.checkBitSynchronizationHandle = @obj.checkBitSynchronizationE5aI;
			case 'E5aQ'
				obj.CorrelatorOutBufferSize = 100;
				N = obj.CorrelatorOutBufferSize;
				obj.SecondaryPRNCodeObjs{1} = SecondaryPRNCode('E5aQ');
				obj.Replica(1,:) = BitSynchronization.extendCode(...
													 obj.SecondaryPRNCodeObjs{1}.getCode(SVID, 0), N);
				obj.Threshold = zeros(1, 1);
				obj.SufficientStatistics = zeros(1, 1);
				obj.checkBitSynchronizationHandle = @obj.checkBitSynchronizationE5aQ;
			case 'E5b' 
				obj.CorrelatorOutBufferSize = 100;
				N = obj.CorrelatorOutBufferSize;
				obj.SecondaryPRNCodeObjs{1} = SecondaryPRNCode('E5bI');
				obj.SecondaryPRNCodeObjs{2} = SecondaryPRNCode('E5bQ');
				obj.Replica(1,:) = BitSynchronization.extendCode(...
													 obj.SecondaryPRNCodeObjs{1}.getCode(1, 0), N);
				obj.Replica(2,:) = BitSynchronization.extendCode(...
													 obj.SecondaryPRNCodeObjs{2}.getCode(SVID, 0), N);
				obj.Threshold = zeros(1, 2);
				obj.SufficientStatistics = zeros(1, 2);
				obj.checkBitSynchronizationHandle = @obj.checkBitSynchronizationE5b;
			case 'E5bI' 
				obj.CorrelatorOutBufferSize = 100;
				N = obj.CorrelatorOutBufferSize;
				obj.SecondaryPRNCodeObjs{1} = SecondaryPRNCode('E5bI');
				obj.Replica(1,:) = BitSynchronization.extendCode(...
													 obj.SecondaryPRNCodeObjs{1}.getCode(1, 0), N);
				obj.Threshold = zeros(1, 1);
				obj.SufficientStatistics = zeros(1, 1);
				obj.checkBitSynchronizationHandle = @obj.checkBitSynchronizationE5bI;
			case 'E5bQ'
				obj.CorrelatorOutBufferSize = 100;
				N = obj.CorrelatorOutBufferSize;
				obj.SecondaryPRNCodeObjs{1} = SecondaryPRNCode('E5bQ');
				obj.Replica(1,:) = BitSynchronization.extendCode(...
													 obj.SecondaryPRNCodeObjs{1}.getCode(SVID, 0), N);
				obj.Threshold = zeros(1, 1);
				obj.SufficientStatistics = zeros(1, 1);
				obj.checkBitSynchronizationHandle = @obj.checkBitSynchronizationE5bQ;
			case 'E1'  
				obj.CorrelatorOutBufferSize = 25;
				N = obj.CorrelatorOutBufferSize;
				obj.SecondaryPRNCodeObjs{1} = [];
				obj.SecondaryPRNCodeObjs{2} = SecondaryPRNCode('E1c');
				obj.Replica(1,:) = ones(1, N);
				obj.Replica(2,:) = BitSynchronization.extendCode(...
													 obj.SecondaryPRNCodeObjs{2}.getCode(1, 0), N);
				obj.Threshold = zeros(1, 2);
				obj.SufficientStatistics = zeros(1, 2);
				obj.checkBitSynchronizationHandle = @obj.checkBitSynchronizationE1;
			case 'E1c' 
				obj.CorrelatorOutBufferSize = 25;
				N = obj.CorrelatorOutBufferSize;
				obj.SecondaryPRNCodeObjs{1} = SecondaryPRNCode('E1c');
				obj.Replica(1,:) = BitSynchronization.extendCode(...
													 obj.SecondaryPRNCodeObjs{1}.getCode(1, 0), N);
				obj.Threshold = zeros(1, 1);
				obj.SufficientStatistics = zeros(1, 1);
				obj.checkBitSynchronizationHandle = @obj.checkBitSynchronizationE1c;
			case 'B1'  
				obj.CorrelatorOutBufferSize = 100;
				obj.SecondaryPRNCodeObjs{1} = SecondaryPRNCode('B1');
				N = obj.CorrelatorOutBufferSize;
				obj.Replica(1,:) = BitSynchronization.extendCode(...
													 obj.SecondaryPRNCodeObjs{1}.getCode(1, 0), N);
				obj.Threshold = zeros(1, 1);
				obj.SufficientStatistics = zeros(1, 1);
				obj.checkBitSynchronizationHandle = @obj.checkBitSynchronizationB1;
			otherwise
				error('Unknown signal')
		end
	end

	function obj = set.CorrelatorOutBufferSize(obj, val)
		obj.CorrelatorOutBufferSize = val;
		obj.TimeShiftBuffer = zeros(1, obj.CorrelatorOutBufferSize);
		obj.FrequencyShiftBuffer = zeros(1, obj.CorrelatorOutBufferSize);
		obj.CarrierPhaseShiftBuffer = zeros(1, obj.CorrelatorOutBufferSize);
		switch (obj.Name)
			case 'E5' 
				obj.CorrelatorOutBuffer = zeros(4, obj.CorrelatorOutBufferSize);
			case 'E5a' 
				obj.CorrelatorOutBuffer = zeros(2, obj.CorrelatorOutBufferSize);
			case 'E5aI' 
				obj.CorrelatorOutBuffer = zeros(1, obj.CorrelatorOutBufferSize);
			case 'E5aQ' 
				obj.CorrelatorOutBuffer = zeros(1, obj.CorrelatorOutBufferSize);
			case 'E5b' 
				obj.CorrelatorOutBuffer = zeros(2, obj.CorrelatorOutBufferSize);
			case 'E5bI' 
				obj.CorrelatorOutBuffer = zeros(1, obj.CorrelatorOutBufferSize);
			case 'E5bQ' 
				obj.CorrelatorOutBuffer = zeros(1, obj.CorrelatorOutBufferSize);
			case 'E1'  
				obj.CorrelatorOutBuffer = zeros(2, obj.CorrelatorOutBufferSize);
			case 'E1c' 
				obj.CorrelatorOutBuffer = zeros(1, obj.CorrelatorOutBufferSize);
			case 'B1'  
				obj.CorrelatorOutBuffer = zeros(1, obj.CorrelatorOutBufferSize);
			otherwise
				error('Unknown signal')
		end
	end

	function obj = addToBuffer(obj, CorrelatorOut, FrequencyShift, ...
														 TimeShift, CarrierPhaseShift)
		if (obj.NumberOfCorrelatorOutInBuffer < obj.CorrelatorOutBufferSize)
			obj.NumberOfCorrelatorOutInBuffer = obj.NumberOfCorrelatorOutInBuffer+1;
			i = obj.NumberOfCorrelatorOutInBuffer;
		else
			i = obj.CorrelatorOutBufferSize;
			obj.CorrelatorOutBuffer = circshift(obj.CorrelatorOutBuffer.',-1).';
			obj.FrequencyShiftBuffer = circshift(obj.FrequencyShiftBuffer.',-1).';
			obj.TimeShiftBuffer = circshift(obj.TimeShiftBuffer.',-1).';
			obj.CarrierPhaseShiftBuffer =...
		 														circshift(obj.CarrierPhaseShiftBuffer.',-1).';
		end
		obj.CorrelatorOutBuffer(:, i) = CorrelatorOut;
		obj.FrequencyShiftBuffer(:, i) = FrequencyShift;
		obj.TimeShiftBuffer(:, i) = TimeShift;
		obj.CarrierPhaseShiftBuffer(:, i) = CarrierPhaseShift;
 	end

	function out = getFrequencyShiftStd(obj)
		out = obj.getStd(obj.FrequencyShiftBuffer, 2);
	end

	function out = getTimeShiftStd(obj)
		out = obj.getStd(obj.TimeShiftBuffer, 2);
	end

	function out = getCarrierPhaseShiftStd(obj)
		if (sum(obj.CarrierPhaseShiftBuffer == 0) > ...
				floor(obj.CorrelatorOutBufferSize/10))
			out = Inf;
		else
			out = obj.getStd(obj.CarrierPhaseShiftBuffer, 2);
		end
	end

	function out = getStd(obj, Buffer, Degree)
		if (obj.NumberOfCorrelatorOutInBuffer ~= obj.CorrelatorOutBufferSize)
			error('Not enough correlator out in buffer')
		else
			x = 1:obj.NumberOfCorrelatorOutInBuffer;
			y = Buffer;
			[p S] = polyfit(x, y, Degree);
			y_interpolated = polyval(p, x);
			out = sqrt(var(y - y_interpolated));
		end
	end

	function out = checkFrequencyTimeShiftStd(obj, FrequencyShiftStdThreshold,...
																								 TimeShiftStdThreshold)
		if (obj.NumberOfCorrelatorOutInBuffer ~= obj.CorrelatorOutBufferSize)
			error('Not enough correlator out in buffer')
		else
			if ((obj.getFrequencyShiftStd() < FrequencyShiftStdThreshold) && ...
					(obj.getTimeShiftStd() < TimeShiftStdThreshold))
				out = 1;
			else
				out = 0;
			end
		end
 	end

	function out = checkCarrierPhaseShiftStdThreshold(obj,...
 																								CarrierPhaseShiftStdThreshold)
		if (obj.NumberOfCorrelatorOutInBuffer ~= obj.CorrelatorOutBufferSize)
			error('Not enough correlator out in buffer')
		else
			if (obj.getCarrierPhaseShiftStd() < CarrierPhaseShiftStdThreshold) 
				out = 1;
			else
				out = 0;
			end
		end
	end

	function out = checkBitSynchronization(obj)
		if (obj.NumberOfCorrelatorOutInBuffer ~= obj.CorrelatorOutBufferSize)
			error('Not enough correlator out in buffer')
		else
			out = obj.checkBitSynchronizationHandle();
		end
	end

	function out = getThreshold(obj, Std)
		out = Std * sqrt( -2*log(obj.ProbabilityOfFalseAlarm));
	end

	function out = getSufficientStatistics(obj, Observation,...
 																			 	 Replica, NumberOfPeriods)
		out = 0;
		N = NumberOfPeriods;
		M = length(Observation)/N;
		for i = 1:N
			out = out + abs(Observation((1 + (i-1)*M):(i*M)) *...
		 							Replica((1 + (i-1)*M):(i*M)).'); 
		end
	end

	function out = Decide(obj, SufficientStatistics, Threshold)
		if (SufficientStatistics > Threshold)
			out = 1;
		else
			out = 0;
		end
	end

	function out = checkBitSynchronizationIndex(obj, Index)
		M = obj.CorrelatorOutBufferSize;
		N = M/obj.SecondaryPRNCodeObjs{Index}.NumberOfChips;
		Std = obj.getStd(obj.CorrelatorOutBuffer(Index,:), 1) * sqrt(M/N) * sqrt(N);
		SuffStat = obj.getSufficientStatistics(obj.CorrelatorOutBuffer(Index,:),...
																					 obj.Replica(Index,:), N);
		Threshold = obj.getThreshold(Std);
		out = obj.Decide(SuffStat, Threshold);
		obj.Threshold(Index) = Threshold;
		obj.SufficientStatistics(Index) = SuffStat;
	end

	function out = getBitSoftInformation(obj)
		switch (obj.Name)
			case 'E5'
				for i=1:4
					out(i,:) = obj.getBitSoftInformationIndex(i);
				end
			case 'E5a'
					out(1,:) = obj.getBitSoftInformationIndex(1);
					%out(2,:) = obj.getBitSoftInformationIndex(2);
			case 'E5aI'
					out(1,:) = obj.getBitSoftInformationIndex(1);
			case 'E5aQ'
					%out(1,:) = obj.getBitSoftInformationIndex(1);
			case 'E5b'
					out(1,:) = obj.getBitSoftInformationIndex(1);
					%out(2,:) = obj.getBitSoftInformationIndex(2);
			case 'E5bI'
					out(1,:) = obj.getBitSoftInformationIndex(1);
			case 'E5bQ'
					%out(1,:) = obj.getBitSoftInformationIndex(1);
			case 'E1'
					out(1,:) = obj.getBitSoftInformationIndex(1);
			case 'E1c'
					%out(1,:) = obj.getBitSoftInformationIndex(1);
			case 'B1'
					out(1,:) = obj.getBitSoftInformationIndex(1);
			otherwise
				error('Unknown signal')
		end
	end

	function obj = storeBitSoftInformation(obj)
		SI = obj.getBitSoftInformation(); 
		switch (obj.Name)
			case 'E5'
				SIToStore(1,:) = SI(1,:);
				SIToStore(2,:) = SI(3,:);
			case 'E5a'
				SIToStore(1,:) = SI(1,:);
			case 'E5aI'
				SIToStore(1,:) = SI(1,:);
			case 'E5aQ'
			case 'E5b'
				SIToStore(1,:) = SI(1,:);
			case 'E5bI'
				SIToStore(1,:) = SI(1,:);
			case 'E5bQ'
			case 'E1'
				SIToStore(1,:) = SI(1,:);
			case 'E1c'
			case 'B1'
				SIToStore(1,:) = SI(1,:);
			otherwise
				error('Unknown signal')
		end
		[m n] = size(SIToStore);
		File = obj.BitSoftInformationFile;
        for i=1:n
			for k=1:m
				fprintf(File, '%f', SIToStore(k, i));
			end
			fprintf(File, '\n');
		end
	end

	function out = getBitSoftInformationIndex(obj, Index)
		M = obj.CorrelatorOutBufferSize;
		try 
			N = M/obj.SecondaryPRNCodeObjs{Index}.NumberOfChips;
		catch
			N = M;
		end
		Observation = obj.CorrelatorOutBuffer(Index,:);
		K = M/N;
		Replica = obj.Replica(Index, :);
		for i = 1:N
			tmp(i) = Observation((1 + (i-1)*K):(i*K)) *...
		 					 Replica((1 + (i-1)*K):(i*K)).'; 
		end
		out = real(tmp(N));
		%CarrierPhaseVec = atan(imag(tmp)./real(tmp)); % insensitive to 180 degree
		%CarrierPhase = mean(CarrierPhaseVec);
		%tmp = tmp .* exp(-j*CarrierPhase);
		%out = real(tmp(N));
		
		%CarrierPhaseVec = atan2(imag(tmp), real(tmp)); % insensitive to 180 degree
		%angle_old = CarrierPhaseVec(N-1);
		%angle_new = CarrierPhaseVec(N);
		
		%out = angle_new - angle_old;
		%if (out < (-3*pi/2))
		%	out = out + 2*pi;
		%elseif ( (out >= (-3*pi/2)) && (out < -pi))
        %	out = out + pi;
        %elseif ( (out >= -pi) && (out < (-pi/2)))
		%	out = out + pi/2;  
        %elseif ( (out >= -pi/2) && (out < 0))
		%	out = out + pi/2;
        %elseif ( (out >= pi/2) && (out < pi))
		%	out = out - pi;   
        %elseif ( (out >= pi) && (out < 3*pi/2))
		%	out = out - 3*pi/2;  
        %elseif ( (out >= 3*pi/2) && (out < 2*pi))
		%	out = out - 3*pi/2;  
		%end
        
    %tmp2 = tmp(N)/tmp(N-1);
    %phi = atan2(imag(tmp2), real(tmp2));
    %out = abs(phi) - pi/2;
	end

	function delete(obj)
		if (~isempty(obj.BitSoftInformationFile))
			fclose(obj.BitSoftInformationFile);
		end
	end

end % end of methods

methods (Access = private)
	
	function out = checkBitSynchronizationE5(obj)
		for i=1:4
			out(i) = obj.checkBitSynchronizationIndex(i);
		end
	end

	function out = checkBitSynchronizationE5a(obj)
		out(1) = obj.checkBitSynchronizationIndex(1);
		out(2) = obj.checkBitSynchronizationIndex(2);
	end

	function out = checkBitSynchronizationE5aI(obj)
		out(1) = obj.checkBitSynchronizationIndex(1);
	end

	function out = checkBitSynchronizationE5aQ(obj)
		out(1) = obj.checkBitSynchronizationIndex(1);
	end

	function out = checkBitSynchronizationE5b(obj)
		out(1) = obj.checkBitSynchronizationIndex(1);
		out(2) = obj.checkBitSynchronizationIndex(2);
	end

	function out = checkBitSynchronizationE5bI(obj)
		out(1) = obj.checkBitSynchronizationIndex(1);
	end

	function out = checkBitSynchronizationE5bQ(obj)
		out(1) = obj.checkBitSynchronizationIndex(1);
	end

	function out = checkBitSynchronizationE1(obj)
		out(1) = obj.checkBitSynchronizationIndex(2);
	end

	function out = checkBitSynchronizationE1c(obj)
		out(1) = obj.checkBitSynchronizationIndex(1);
	end

	function out = checkBitSynchronizationB1(obj)
		out(1) = obj.checkBitSynchronizationIndex(1);
	end

end % end of methods

methods (Static)

	function out = extendCode(Code, NumberOfChips)
		CodeLength = length(Code);
		NumberOfPeriods = floor(NumberOfChips/CodeLength);
		for i=1:NumberOfPeriods
			out((1 + (i-1)*CodeLength) : (i*CodeLength) ) = -2*Code +1;
		end
		if (NumberOfChips > (NumberOfPeriods*CodeLength) )
			ChipsToEnd = NumberOfChips -NumberOfPeriods*CodeLength;
			out((1 + NumberOfPeriods*CodeLength) : end) = -2*Code(1:ChipsToEnd) +1; 
		end
	end

end % end of methods

end % end of class
